<?php

namespace App\Game\Core;

use App\Game\Core\DdzPoker;
use App\Model\User;
use phpDocumentor\Reflection\Types\False_;


/**
 *  游戏策略静态类，规范游戏策略类，此类可以扩展每个策略公共的方法
 *  每个游戏请求逻辑，都算一个游戏策略， 采用策略模式实现
 *  策略支持三种协议的策略， WEBSOCKET, HTTP, TCP, 分别在app目录下
 *  框架的开发也是主要分别实现每种协议逻辑，根据路由配置转发到相对应的策略里
 */
abstract class AStrategy
{
    /**
     * 参数
     */
    protected $_params = array();

    /**
     * 斗地主对象
     * @var null
     */
    protected $obj_ddz = null;

    /**
     * 房间配置
     * @var null
     */
    protected $game_conf = null;

    /**
     * 用户行为配置
     * @var null
     */
    protected $user_conf = null;

    /**
     * 构造函数，协议传输过来的数据
     * AStrategy constructor.
     * @param array $params
     */
    public function __construct($params = array())
    {
        $this->_params = $params;
        $this->obj_ddz = new DdzPoker();
        $this->game_conf = config('game');
        $this->user_conf = config('user');
    }

    /**
     * 执行方法，每条游戏协议，实现这个方法就行
     */
    abstract public function exec();

    /**
     * 服务器广播消息， 此方法是给所有的连接客户端， 广播消息
     * @param $serv
     * @param $data
     */
    protected function Broadcast($serv, $data)
    {
        foreach ($serv->connections as $fd) {
            $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
        }
    }

    /**
     * 当connetions属性无效时可以使用此方法，服务器广播消息， 此方法是给所有的连接客户端， 广播消息，通过方法getClientList广播
     * @param $serv
     * @param $data
     */
    protected function BroadCast2($serv, $data)
    {
        $start_fd = 0;
        while (true) {
            $conn_list = $serv->getClientList($start_fd, 10);
            if ($conn_list === false or count($conn_list) === 0) {
                Log::show("BroadCast finish");
                break;
            }
            $start_fd = end($conn_list);
            foreach ($conn_list as $fd) {
                //获取客户端信息
                $client_info = $serv->getClientInfo($fd);
                if (isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                    $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
                }
            }
        }
    }

    /**
     * 游戏大厅房间数据
     * @return array
     */
    protected function hallData()
    {
        $game_conf = $this->game_conf;
        $room_total_key = $game_conf['user_room_total'];
        $fake_room_data = $this->fakeHallData();
        if (redis()->exists($room_total_key)) {
            $rooms = redis()->sMembers($room_total_key);
            $hall_rooms = array();
            $hall_datas = array();
            $wait_room_conf = $game_conf['user_room_wait'];
            $room_data_conf = $game_conf['user_room_data'];
            foreach ($rooms as $val) {
                $wait_room_key = sprintf($wait_room_conf, $val);
                $room_data_key = sprintf($room_data_conf, $val);
                // 正在等待的房间
                if (redis()->exists($wait_room_key)) {
                    $hall_rooms[$val] = json_decode(redis()->get($wait_room_key), true);
                }
                // 正在游戏的房间
                if (redis()->exists($room_data_key)) {
                    $room_data = redis()->hGetAll($room_data_key);
                    if (!isset($room_data['is_game_over']) || ($room_data['is_game_over'] == 0)) {
                        if (!empty($room_data['uinfo'])) {
                            $wait_users = [];   // 正在等待房间里的用户account
                            $room_data_uinfo = json_decode($room_data['uinfo'], true);
                            if (gettype($room_data_uinfo) == 'string') {
                                $room_data_uinfo = json_decode($room_data_uinfo, true);
                            }
                            foreach ($room_data_uinfo as $ks => $vs) {
                                $wait_users[] = $ks;
                            }
                            $hall_rooms[$val] = $wait_users;
                        }
                    }
                }
            }
            if (!empty($hall_rooms)) {
                // 查询个房号用户信息
                $i = 0;
                foreach ($hall_rooms as $k => $v) {
                    if (!empty($v)) {
                        // 房间用户存在，查询用户信息
                        $curr_room_users = User::getUsers($v);
                        if ($curr_room_users['code'] == 0) {
                            $curr_room_data = $curr_room_users['data'];
                            foreach ($curr_room_data as $ks => $vs) {
                                $curr_room_data[$ks]['chair_id'] = $this->findChairId($k, $vs['account']);
                            }
                            $hall_datas[$i]['room_no'] = $k;
                            $hall_datas[$i]['room_data'] = $curr_room_data;
                        }
                        $i++;
                    }
                }
                $total_room_data = array_merge($fake_room_data, $hall_datas);
                return $total_room_data;
            } else {
                return $fake_room_data;
            }
        } else {
            return $fake_room_data;
        }
    }

    /**
     * 返回玩家房间桌位号
     * @param $room_no
     * @param $account
     * @return mixed
     */
    protected function findChairId($room_no, $account)
    {
        $game_conf = $this->game_conf;
        $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
        $chair_data = json_decode(redis()->get($room_chair_key), true);
        return $chair_data[$account];
    }

    /**
     * 返回准备玩家座位号
     * @param $prepared_users
     * @param $room_no
     * @return array
     */
    protected function getPrepareChairs($prepared_users, $room_no)
    {
        $game_conf = $this->game_conf;
        $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
        $chair_data = json_decode(redis()->get($room_chair_key), true);
        $chairs = [];
        foreach ($prepared_users as $v) {
            $chairs[] = $chair_data[$v];
        }
        return $chairs;
    }

    /**
     * 对多用发送信息
     * @param $serv
     * @param $users
     * @param $data
     */
    protected function pushToUsers($serv, $users, $data)
    {
        foreach ($users as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if (isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * 获取房间信息
     * @param $account
     * @return array
     */
    protected function getRoomData($account)
    {
        $user_room_data = array();
        //获取用户房间号
        $room_no = $this->getRoomNo($account);
        //房间信息
        $game_key = $this->getGameConf('user_room_data');
        if ($game_key) {
            $user_room_key = sprintf($game_key, $room_no);
            $user_room_data = redis()->hGetAll($user_room_key);
        }
        return $user_room_data;
    }

    /**
     * 获取房间信息通过key
     * @param $account
     * @param $key
     * @return mixed
     */
    protected function getRoomDataByKey($account, $key)
    {
        $data = array();
        $no = $this->getRoomNo($account);
        $game_key = $this->getGameConf('user_room_data');
        if ($no && $game_key) {
            $user_room_key = sprintf($game_key, $no);
            $user_room_data = redis()->hGet($user_room_key, $key);
            $data = json_decode($user_room_data, true);
            if (is_null($data)) {
                $data = $user_room_data;
            }
        }
        return $data;
    }

    /**
     * 获取用户房间号
     * @param $account
     * @return mixed
     */
    protected function getRoomNo($account)
    {
        $game_key = $this->getGameConf('user_room');
        //获取用户房间号
        $room_key = sprintf($game_key, $account);
        $room_no = redis()->get($room_key);
        return $room_no ? $room_no : 0;
    }

    /**
     * 获取房间信息通过key
     * @param $account
     * @return mixed
     */
    protected function getRoomUserInfoDataByKey($account)
    {
        $user_data = array();
        $no = $this->getRoomNo($account);
        $game_key = $this->getGameConf('user_room_data');
        if ($no && $game_key) {
            //房间信息
            $user_room_key = sprintf($game_key, $no);
            $user_data = redis()->hGet($user_room_key, $account);
            $user_data = json_decode($user_data, true);
        }
        return $user_data;
    }

    /**
     * 设置房间用户玩牌信息
     * @param $account
     * @param $key
     * @param $value
     */
    protected function setRoomData($account, $key, $value)
    {
        $no = $this->getRoomNo($account);
        $game_key = $this->getGameConf('user_room_data');
        if ($no && $game_key) {
            $user_room_key = sprintf($game_key, $no);
            redis()->hSet((string)$user_room_key, (string)$key, $value);
        }
    }

    /**
     * 添加房间游戏数据记录
     * @param $room_no
     */
    protected function addRoomDataLog($room_no)
    {
        $game_conf = $this->game_conf;
        $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
        $room_log_key = sprintf($game_conf['user_room_log'], $room_no);
        $room_log_id_key = sprintf($game_conf['user_room_log_id'], $room_no);
        if (redis()->exists($room_log_id_key)) {
            $len = redis()->sCard($room_log_id_key) + 1;
            redis()->sAdd($room_log_id_key, $len);
        } else {
            $len = 1;
            redis()->sAdd($room_log_id_key, $len);
        }
        $datas = redis()->hGetAll($room_data_key);
        redis()->hMSet($room_log_key . ':' . $len, $datas);
    }

    /**
     * 清除房间等待绑定的玩家账号
     * @param $room_no
     */
    protected function cleanRoomWait($room_no)
    {
        $game_conf = $this->game_conf;
        $room_wait_key = sprintf($game_conf['user_room_wait'], $room_no);
        if (redis()->exists($room_wait_key)) {
            redis()->del($room_wait_key);
        }
    }

    /**
     * 清除房间准备记录
     * @param $room_no
     */
    protected function cleanRoomPrepare($room_no)
    {
        $game_conf = $this->game_conf;
        $room_prepare_key = sprintf($game_conf['user_room_prepare'], $room_no);
        if (redis()->exists($room_prepare_key)) {
            redis()->del($room_prepare_key);
        }
    }

    /**
     * 清除房间换牌记录
     * @param $room_no
     */
    protected function cleanRoomChange($room_no)
    {
        $game_conf = $this->game_conf;
        $room_prepare_key = sprintf($game_conf['user_room_change'], $room_no);
        if (redis()->exists($room_prepare_key)) {
            redis()->del($room_prepare_key);
        }
    }

    /**
     * 清除房间定缺记录
     * @param $room_no
     */
    protected function cleanRoomColor($room_no)
    {
        $game_conf = $this->game_conf;
        $room_prepare_key = sprintf($game_conf['user_room_color'], $room_no);
        if (redis()->exists($room_prepare_key)) {
            redis()->del($room_prepare_key);
        }
    }

    /**
     * 清除房间玩家座位号记录
     * @param $room_no
     */
    protected function cleanRoomChair($room_no)
    {
        $game_conf = $this->game_conf;
        $room_chair_key = sprintf($game_conf['user_room_chair'], $room_no);
        if (redis()->exists($room_chair_key)) {
            redis()->del($room_chair_key);
        }
    }

    /**
     * 清除房间游戏数据
     * @param $room_no
     */
    protected function cleanRoomData($room_no)
    {
        $game_conf = $this->game_conf;
        $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
        if (redis()->exists($room_data_key)) {
            redis()->del($room_data_key);
        }
    }

    /**
     * 清除map
     * @param $room_no
     */
    protected function cleanRoomMap($room_no)
    {
        $game_conf = $this->game_conf;
        $room_wait_key = sprintf($game_conf['user_room_wait'], $room_no);
        if (redis()->exists($room_wait_key)) {
            $user = json_decode(redis()->get($room_wait_key), true);
            foreach ($user as $k => $v) {
                $user_map_key = sprintf($game_conf['user_room'], $v);
                redis()->del($user_map_key);
            }
        }
    }

    /**
     * 批量设置房间信息
     * @param $account
     * @param $params
     */
    protected function muitSetRoomData($account, $params)
    {
        $no = $this->getRoomNo($account);
        $game_key = $this->getGameConf('user_room_data');
        if ($no && $game_key) {
            $user_room_key = sprintf($game_key, $no);
            redis()->hMSet($user_room_key, $params);
        }
    }

    /**
     * 设置房间信息
     * @param $room_user_data
     * @param $account
     * @param $key
     * @param $value
     */
    protected function setRoomUserInfoDataByKey($room_user_data, $account, $key, $value)
    {
        $no = $this->getRoomNo($account);
        $game_key = $this->getGameConf('user_room_data');
        if ($no && $game_key) {
            $room_user_data[$key] = $value;
            $user_room_key = sprintf($game_key, $no);
            redis()->hSet((string)$user_room_key, (string)$account, json_encode($room_user_data));
        }
    }

    /**
     * 通过accounts获取fds
     * @param $account
     * @return array
     */
    protected function getRoomFds($account)
    {
        $accs = $this->getRoomDataByKey($account, 'call_order');
        $game_key = $this->getGameConf('user_bind_key');
        $binds = $fds = array();
        if (!empty($accs) && $game_key) {
            foreach ($accs as $v) {
                $binds[] = sprintf($game_key, $v);
            }
            $fds = redis()->mget($binds);
        }
        return $fds;
    }

    /**
     * 获取房间其他用户的fds
     * @param $room_no
     * @param $account
     * @return array
     */
    protected function getOtherFdsByRoomNo($room_no)
    {
        $game_conf = $this->game_conf;
        $room_wait_key = sprintf($game_conf['user_room_wait'], $room_no);
        $room_users = redis()->get($room_wait_key);
        $room_users = json_decode($room_users, true);
        $fds = [];
        foreach ($room_users as $k => $v) {
            $bind_key = sprintf($game_conf['user_bind_key'], $v);
            $fds[] = redis()->get($bind_key);
        }
        return $fds;
    }

    /**
     * 获取房间玩家fds
     * @param $room_no
     * @return array|bool
     */
    protected function getFdsByRoomNo($room_no)
    {
        $game_conf = $this->game_conf;
        $room_wait_key = sprintf($game_conf['user_room_wait'], $room_no);
        if (redis()->exists($room_wait_key)) {
            $room_users = json_decode(redis()->get($room_wait_key), true);
            $fds = [];
            foreach ($room_users as $k => $v) {
                $bind_key = sprintf($game_conf['user_bind_key'], $v);
                $fds[] = redis()->get($bind_key);
            }
            return $fds;
        } else {
            return false;
        }
    }

    /**
     * 批量清除用户房间号
     * @param $users
     */
    protected function clearRoomNo($users)
    {
        $game_key = $this->getGameConf('user_room');
        if (is_array($users)) {
            foreach ($users as $v) {
                $key = sprintf($game_key, $v);
                redis()->del($key);

            }
        }
    }

    /**
     * 把php数组存入redis的hash表中
     * @param $arr
     * @param $hash_key
     */
    protected function arrToHashInRedis($arr, $hash_key)
    {
        foreach ($arr as $key => $val) {
            redis()->hSet((string)$hash_key, (string)$key, json_encode($val));
        }
    }

    /**
     * 返回游戏配置
     * @param string $key
     * @return string
     */
    protected function getGameConf($key = '')
    {
        $conf = config('game');
        if (isset($conf[$key])) {
            return $conf[$key];
        } else {
            return '';
        }
    }

    /**
     * 设置游戏房间玩牌步骤信息, 方便后面录像回放
     * @param $account
     * @param $key
     * @param $value
     */
    protected function setRoomPlayCardStep($account, $key, $value)
    {
        $no = $this->getRoomNo($account);
        $game_key = $this->getGameConf('user_room_play');
        if ($no && $game_key) {
            $play_key = sprintf($game_key, $no);
            redis()->hSet((string)$play_key, (string)$key, $value);
        }
    }

    /**
     * 检查等待房间人数，不算自身
     * @param $room_no
     * @param $account
     * @return array
     */
    protected function checkWaitMembers($room_no, $account)
    {
        $game_conf = $this->game_conf;
        $wait_room_key = sprintf($game_conf['user_room_wait'], $room_no);
        $msg = [];
        if (redis()->exists($wait_room_key)) {
            $player = json_decode(redis()->get($wait_room_key), true);
            $temArr = [];
            foreach ($player as $cid => $acc) {
                if (intval($acc) > 0) {
                    $temArr[$cid] = $acc;
                }
            }
            if (count($player) != count($temArr)) {
                redis()->set($wait_room_key, json_encode($temArr));
            }
            if (in_array($account, $player)) {
                $diff_player = array_diff($player, [$account]);
                array_push($player, $account);
                $msg['len'] = count($diff_player);
                $msg['in_room'] = 1;
            } else {
                $msg['len'] = count($player);
                $msg['in_room'] = 0;
            }
            if (!empty($player)) {
                $players = $this->membersInfo($player);
                $msg['players'] = $players;
            } else {
                $msg['players'] = [];
            }
        }
        return $msg;
    }

    /**
     *
     * 查询房间用户xinx
     * @param $userArr
     * @return array
     */
    protected function membersInfo($userArr)
    {
        if (is_array($userArr) && !empty($userArr)) {
            $users = User::getUsers($userArr);
            if ($users['code'] == 0) {
                return $users['data'];
            }
        }
        return [];
    }

    /**
     * 通过房间号获取所有用户account
     * @param $room_no
     * @return array
     */
    protected function getUsersByRoom($room_no)
    {
        $game_conf = $this->game_conf;
        $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
        $new_uinfo = json_decode(redis()->hGet($room_data_key, 'uinfo'), true);
        $users = [];
        foreach ($new_uinfo as $k => $v) {
            $users[] = $k;
        }
        return $users;
    }

    /**
     * 玩家翻倍数
     * @param $room_no
     * @param $account
     * @return float|int
     */
    protected function getUserRate($room_no, $account)
    {
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'], $room_no);
        if (redis()->exists($room_data_key)) {
            $room_data = redis()->hGetAll($room_data_key);
            $uinfo = json_decode($room_data['uinfo'], true);
            if (isset($room_data['master'])) {
                if ($account == $room_data['master']) {
                    $rate = $uinfo[$account]['rate'] * 2;
                } else {
                    $rate = $uinfo[$account]['rate'];
                }
            } else {
                $rate = $uinfo[$account]['rate'];
            }
        } else {
            $rate = 1;
        }
        return $rate;
    }

    /**
     * 模拟大厅数据
     */
    protected function fakeHallData()
    {
        $fake_room_key = $this->game_conf['fake_room_hallFake'];
        $fake_expire = $this->game_conf['fake_expire'];
        if (redis()->exists($fake_room_key)) {
            $fake_room_data = json_decode(redis()->get($fake_room_key), true);
            return $fake_room_data;
        } else {
            $num = rand(10000, 99999);
            $data = User::getUserNum($num);
            $fake_room_data = [];
            $team_arr = array_chunk($data, 3);
            $room_no = 9989;
            foreach ($team_arr as $k => $v) {
                $team_data = [];
                if (!empty($v)) {
                    foreach ($v as $kk => $vv) {
                        $team_data[] = [
                            'account' => $vv['account'],
                            'score' => $vv['score'],
                            'avatar' => $vv['avatar'],
                            'chair_id' => (int)($kk + 1),
                        ];
                    }
                }
                $fake_room_data[] = ['room_data' => $team_data, 'room_no' => (int)($room_no + $k)];
            }
            // 缓存模拟房间数据
            redis()->set($fake_room_key, json_encode($fake_room_data, JSON_UNESCAPED_SLASHES));
            redis()->expire($fake_room_key, $fake_expire);
            return $fake_room_data;
        }
    }

    protected function filterNickname($account)
    {
        $regex = "/\/|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\|[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{2600}-\x{26FF}]|[\x{2700}-\x{27BF}]|/";
        $str = preg_replace($regex, '', $account);
        return preg_replace('# #', '', $str);
    }
}
